package mysql

import (
	"fmt"
	"bytes"
	"context"
	"errors"
	"os"
	"os/exec"
	"strings"
	"regexp"
	"syscall"
	"path/filepath"
	"database/sql"

	"github.com/gogf/gf/v2/frame/g"
	_ "github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/glog"

	_ "github.com/go-sql-driver/mysql"

	mysqlApi "yyt/api/mysql"
)

type IMysqlService interface {
	Dump(ctx context.Context, req *mysqlApi.MysqlReq) (res *mysqlApi.MysqlRes, error error)

	Clear(ctx context.Context, req *mysqlApi.MysqlReq) (res *mysqlApi.MysqlRes, error error)
}

type MysqlService struct{}

func NewMysqlService() IMysqlService {
	return &MysqlService{}
}

var log *glog.Logger = g.Log()

func (m *MysqlService) Dump(ctx context.Context, req *mysqlApi.MysqlReq) (res *mysqlApi.MysqlRes, error error) {
	if err := DumpTable(ctx, req.TableName, req.Path); err != nil {
		return nil, err
	}
	return &mysqlApi.MysqlRes{}, nil
}

func (m *MysqlService) Clear(ctx context.Context, req *mysqlApi.MysqlReq) (res *mysqlApi.MysqlRes, error error) {
	if err := TruncateTable(ctx, req.TableName); err != nil {
		return nil, err
	}

	return &mysqlApi.MysqlRes{}, nil
}

func ensureFile(filePath string) error {
	// 步骤 1：检查文件是否存在
	_, err := os.Stat(filePath)
	if err == nil {
		// 文件存在，执行删除
		if err := os.Remove(filePath); err != nil {
			return fmt.Errorf("删除文件失败: %w", err)
		}
		fmt.Printf("已删除旧文件: %s\n", filePath)
	} else if !os.IsNotExist(err) {
		// 非"文件不存在"的其他错误
		return fmt.Errorf("文件状态检查失败: %w", err)
	}

	// 步骤 2：确保目录存在
	dir := filepath.Dir(filePath)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return fmt.Errorf("创建目录失败: %w", err)
	}
	if dir != "." { // 当目录不是当前目录时输出日志
		fmt.Printf("已创建目录: %s\n", dir)
	}

	// 步骤 3：创建文件
	file, err := os.Create(filePath)
	if err != nil {
		return fmt.Errorf("创建文件失败: %w", err)
	}
	defer file.Close()

	fmt.Printf("已创建文件: %s\n", filePath)
	return nil
}

func DumpTable(ctx context.Context, tableName string, dumpPath string) error {
	if tableName == "" {
		return errors.New("请确认你要备份的表名")
	}

	if dumpPath == "" {
		dumpPath = "./dump"
	}

	container := "mysql"
	username := "root"
	password := "123456"
	db := "mytest"
	var dumpFilePath string
	if strings.HasSuffix(dumpPath, "/") {
		dumpFilePath = dumpPath + tableName + ".sql"
	} else {
		dumpFilePath = dumpPath + "/" + tableName + ".sql"
	}
	

	cmd := exec.Command(
		"docker", "exec", container,
		"mysqldump",
		"-u", username,
		"--password="+password,
		db,
		tableName,
	)

	// 设置输出文件
	err := ensureFile(dumpFilePath)
	if err != nil {
		log.Errorf(ctx, "创建文件失败: %v", err)
		return err;
	}

	outputFile, err := os.Create(dumpFilePath)
	if err != nil {
		log.Errorf(ctx, "创建输出文件失败: %v", err)
		return err;
	}

	defer outputFile.Close()

	var stderr bytes.Buffer
	cmd.Stdout = outputFile
	cmd.Stderr = &stderr

	// 执行命令
	if err := cmd.Run(); err != nil {
		// 获取退出状态码
		if exitErr, ok := err.(*exec.ExitError); ok {
			if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
				log.Errorf(ctx, "命令异常退出 (状态码 %+d)\n错误日志: %+s", status.ExitStatus(), stderr.String())
				return exitErr
			}
		}
		log.Errorf(ctx, "命令执行失败: %+v\n错误日志: %+s", err, stderr.String())
		return err
	}

	log.Infof(ctx, "备份成功保存至: %s", dumpFilePath)
	return nil
}

// 清空表（TRUNCATE 方式）
func TruncateTable(ctx context.Context, tableName string) error {
	if tableName == "" {
		return errors.New("请确认你要清空的表名")
	}

	username := "root"
	password := "123456"
	dbName := "mytest"

	// 构建DSN
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?parseTime=true",
		username,
		password,
		"localhost",
		13306,
		dbName,
	)

	// 连接数据库
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		return err
	}
	defer db.Close()

	// 检查连接
	if err := db.Ping(); err != nil {
		return err
	}

	// 1. 验证表名合法性（防止SQL注入）
	if !isValidTableName(tableName) {
		log.Errorf(ctx, "非法表名: %s", tableName)
		return fmt.Errorf("非法表名: %s", tableName)
	}

	// 2. 执行 TRUNCATE
	_, err = db.Exec(fmt.Sprintf("TRUNCATE TABLE `%s`", tableName))
	return err
}

// 验证表名格式（字母/数字/下划线）
func isValidTableName(name string) bool {
	matched, _ := regexp.MatchString("^[a-zA-Z0-9_]+$", name)
	return matched
}